import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * @Author: czy
 * @Date: 2019/11/4 8:35
 */
public class CryptoUtil {
    /**
     * 对称加密算法就是传统的用一个密码进行加密和解密
     * 常用的对称加密算法有 DES AES  IDEA
     * 密钥长度直接决定加密强度,DES算法由于密钥过短，可以在短时间内被暴力破解，所以现在已经不安全了。
     * AES ECB模式    使用128位秘钥每次生成的密文相同
     * AES CBC模式    使用256位秘钥每次生成的密文不相同,因为每次会生成一个随机生成的16字节IV参数
     */

    //===================================AES加密 ECB模式开始============================================================
    /**
     * AES加密解密 128位密钥 = 16 bytes Key:
     */
    public static final String AES_KEY_ECB = "1234567890abcdef";

    /**
     * AES加密 ECB模式
     * @param key
     * @param input
     * @return
     * @throws Exception
     */
    public static String aesEncryptECB(String key, String input) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] bytes = cipher.doFinal(input.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * AES解密 ECB模式
     * @param key
     * @param input
     * @return
     * @throws Exception
     */
    public static String aesDecryptECB(String key, String input) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));
        return new String(bytes,"UTF-8");
    }

    //===================================AES加密 ECB模式结束============================================================

    /**
     * AES CBC模式加密时 java.security.InvalidKeyException: Illegal key size 异常
     * 解决方法:    http://www.bubuko.com/infodetail-1265585.html
     */


    //===================================AES加密 CBC模式开始============================================================

    /**
     * AES加密解密 256位密钥 = 32 bytes Key:
     */
    public static final String AES_KEY_CBC = "1234567890abcdef1234567890abcdef";

    /**
     * AES加密 CBC模式
     * @param key
     * @param input
     * @return
     * @throws Exception
     */
    public static String aesEncryptCBC(String key, String input) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        // CBC模式需要生成一个16 bytes的initialization vector:
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] iv = sr.generateSeed(16);
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
        byte[] data = cipher.doFinal(input.getBytes("UTF-8"));
        // IV不需要保密，把IV和密文一起返回:
        byte[] join = join(iv, data);
        return Base64.getEncoder().encodeToString(join);
    }

    /**
     * AES解密 CBC模式
     * @param key
     * @param input
     * @return
     * @throws Exception
     */
    public static String aesDecryptCBC(String key, String input) throws Exception {
        // 把input分割成IV和密文:
        byte[] iv = new byte[16];
        byte[] data = new byte[Base64.getDecoder().decode(input).length - 16];
        System.arraycopy(Base64.getDecoder().decode(input), 0, iv, 0, 16);
        System.arraycopy(Base64.getDecoder().decode(input), 16, data, 0, data.length);
        // 解密:
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
        byte[] decrypted = cipher.doFinal(data);
        return new String(decrypted, "UTF-8");
    }

    public static byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }

    //===================================AES加密 CBC模式结束============================================================


    //===================================md5摘要算法 开始===============================================================

    /**
     * md5和 HA-1、SHA-256、SHA-512用法一样,只需修改下方的"MD5"名称即可
     * @param input
     * @return
     */
    public static String md5(String input){
        // 创建一个MessageDigest实例:
        byte[] digest=null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            digest = md.digest(input.getBytes("UTF-8"));

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        //转换为16进制字符串
        return new BigInteger(1, digest).toString(16);
    }

    //===================================md5摘要算法 结束===============================================================

    //===================================HMAC算法 开始==================================================================
    //https://blog.csdn.net/baidu_22254181/article/details/82594072
    public static String hmacEncrypt(){
        return null;
    }

    public static String hmacDecrypt(){
        return null;
    }

    //===================================HMAC算法 结束==================================================================



    public static void main(String[] args) throws Exception {
        //原文:
        String message = "Hello, world!";

//        // AES ECB模式 加密:
//        String encrypted = aesEncryptECB(AES_KEY_ECB, message);
//        System.out.println("Encrypted: " + encrypted);//2xiGROlFBhC57b7EGu5c3g==
//        //AES ECB模式 解密:
//        String decrypted = aesDecryptECB(AES_KEY_ECB, encrypted);
//        System.out.println("Decrypted: " + decrypted);//Hello, world!


        // AES CBC模式 加密:
//        byte[] data = message.getBytes("UTF-8");
//        String encrypted = aesEncryptCBC(AES_KEY_CBC, message);
//        System.out.println("Encrypted: " + encrypted);
//        //AES CBC模式 解密:
//        String decrypted = aesDecryptCBC(AES_KEY_CBC, encrypted);
//        System.out.println("Decrypted: " + decrypted);

    }
}
